/*
 * Copyright (c) Jan Šlahora (${USER}) - 2012.
 * Created in IntelliJ IDEA 10.5
 */

package statistics;

import com.sun.org.apache.bcel.internal.generic.RET;

import java.util.*;

/*
*   File Agregace 
*   in package statistics
*   was created by slaha
*   on 11.3.12 at 13:37.
*
*/
public class Agregace {

  private ArrayList<String> texty;
  private ArrayList<ArrayList<Character>> mnozinyA1;
  private ArrayList<ArrayList<ArrayList<Character>>> mnozinyAx;
  private ArrayList<ArrayList<String>> mnozinyB1;
  private ArrayList<ArrayList<ArrayList<String>>> mnozinyBx;
  private ArrayList<ArrayList<Double>> Si;
  private ArrayList<Double> prumerySi;
  private ArrayList<Double> teoretickeSi;
  private double mncB, mncA, koeficientD;

  public Agregace(ArrayList<String> texty) {
    this.texty = texty;
    mnozinyA1 = new ArrayList<>(texty.size());
    naplnMnozinyA1();
    mnozinyAx = new ArrayList<>(texty.size());
    naplnMnozinyAx();
    mnozinyB1 = new ArrayList<>(texty.size());
    naplMnozinuB1();
    mnozinyBx = new ArrayList<>(texty.size());
    naplnMnozinyBx();
    Si = new ArrayList<>(9);
    vypoctiSi();
    prumerySi = new ArrayList<>(9);
    vypoctiPrumerneSi();
    vypoctiMNC();
    teoretickeSi = new ArrayList<>(prumerySi.size());
    vypoctiTeoretickeSi();
    vypoctiKoeficientD();
  }

  private void naplnMnozinyA1() {
    for(int i = 0; i < texty.size(); i++) {
      String radek = dejRadek(1, texty.get(i));
      char[] pole = radek.toCharArray();
      ArrayList<Character> fonemy = new ArrayList<>();
      for(char znak : pole) {
        fonemy.add(znak);
      }
      mnozinyA1.add(fonemy);
    }
  }

  private void naplnMnozinyAx() {
    //..prochází všecky texty
    for (int i = 0; i < texty.size(); i++) {
      ArrayList<ArrayList<Character>> verse = new ArrayList<>();
      //.. prochází 2. - 10. řádek
      int j = 1;
      do {
        String radek;
        do {
          radek = dejRadek(++j, texty.get(i));
          if (radek == null) {
            mnozinyAx.add(verse);
            return;
          }
        } while (radek.length() == 0);
        
        char[] pole = radek.toCharArray();
        ArrayList<Character> fonemy = new ArrayList<>();
        for (char znak : pole) {
          fonemy.add(znak);
        }
        verse.add(fonemy);
      } while (verse.size() != 10);
      mnozinyAx.add(verse);
    }
  }

  private void naplMnozinuB1() {
    for (int i = 0; i < texty.size(); i++) {
      String radek = dejRadek(1, texty.get(i));
      char[] pole = radek.toCharArray();
      ArrayList<String> mnozinaB = new ArrayList<>();
      for(int j = 0; j < pole.length-1; j++) {
        String dvojice = new String(pole, j, 2);
        mnozinaB.add(dvojice);
      }
      mnozinyB1.add(mnozinaB);
    }
  }

  private void naplnMnozinyBx() {
    //..prochází všecky texty
    for (int i = 0; i < texty.size(); i++) {
      ArrayList<ArrayList<String>> verse = new ArrayList<>();
      //.. prochází 2. - 10. řádek
      int j = 1;
      do {
        String radek;
        do {
          radek = dejRadek(++j, texty.get(i));
          if (radek == null) {
            mnozinyBx.add(verse);
            return;
          }
        } while (radek.length() == 0);

        char[] pole = radek.toCharArray();
        ArrayList<String> mnozinaBj = new ArrayList<>();
        for (int xx = 0; xx < pole.length-1; xx++) {
          String dvojice = new String(pole, xx, 2);
          mnozinaBj.add(dvojice);
        }
        verse.add(mnozinaBj);
      } while (verse.size() != 10);
      mnozinyBx.add(verse);
    }
  }

  private void vypoctiSi() {
    for (int i = 0; i < mnozinyA1.size(); i++) {
      ArrayList<Character> mnozinaA1 = mnozinyA1.get(i);
      ArrayList<String> mnozinaB1 = mnozinyB1.get(i);
      int velA1 = mnozinaA1.size();
      int velB1 = mnozinaB1.size();
      ArrayList<Double> podobnosti = new ArrayList<>();
      if (i < mnozinyAx.size()) {
        for(int j = 0; j < mnozinyAx.get(i).size(); j++) {
          ArrayList<Character> mnozinaAj = mnozinyAx.get(i).get(j);
          int velAj = mnozinaAj.size();
          ArrayList<Character> prunikA = prunik(mnozinaA1, mnozinaAj);
          int velPrunikA = prunikA.size();
          ArrayList<String> mnozinaBj = mnozinyBx.get(i).get(j);
          int velBj = mnozinaAj.size();
          ArrayList<String> prunikB = prunikStringu(mnozinaB1, mnozinaBj);
          int velPrunikB = prunikB.size();
          double vypocet = 100*((Math.pow(velPrunikA, 2)/(velA1*velAj))+(Math.pow(velPrunikB, 2)/(velB1*velBj)));
          podobnosti.add(vypocet);
        }
      }
      Si.add(podobnosti);
    }
  }

  private void vypoctiPrumerneSi() {
    double[] sumy = new double[Si.get(0).size()];
    Arrays.fill(sumy, 0);
    for(int i = 0; i < Si.size(); i++) {
      ArrayList<Double> hodnoty = Si.get(i);
      for (int j = 0; j < hodnoty.size(); j++) {
        sumy[j] += hodnoty.get(j);
      }
    }

    for(int i = 0; i < sumy.length; i++) {
      prumerySi.add((sumy[i]/(Si.size())));
    }
  }

  private void vypoctiMNC() {
    double A, B;
    int n = prumerySi.size();
    double lnLi = mncLnLi();
    double lnLi2 = mncLnLi2();
    double lnSi = mncLnSi();
    double lnLiLnSi = mncLnLiLnSi();
    double sumaLnLi2 = Math.pow(lnLi, 2);
    B = ((n * lnLiLnSi) - (lnLi * lnSi)) / ((n * lnLi2) - (sumaLnLi2));
    A = ((lnLi2 * lnSi) - (lnLi * lnLiLnSi)) / ((n * lnLi2) - (sumaLnLi2));
    mncA = Math.exp(A);
    mncB = (-1) * B;
  }

  private void vypoctiTeoretickeSi() {
    for (int i = 1; i <= prumerySi.size(); i++) {
      teoretickeSi.add(mncA*Math.pow(i, (-1*mncB)));
    }
  }

  private void vypoctiKoeficientD() {
    double citatel = 0;
    double jmenovatel = 0;
    double soucetSi = 0;

    for(int i = 0; i < prumerySi.size(); i++) {
      double prumSi = prumerySi.get(i);
      double d = Math.pow((prumSi - teoretickeSi.get(i)), 2);
      citatel += d;
      soucetSi += prumSi;
      jmenovatel += Math.pow(prumSi, 2);
    }
    soucetSi = Math.pow(soucetSi, 2);
    soucetSi /= prumerySi.size();
    jmenovatel -= soucetSi;
    koeficientD = 1 - (citatel / jmenovatel);
  }

  private ArrayList<Character> prunik(ArrayList<Character> a1, ArrayList<Character> a2) {
    ArrayList<Character> ret = new ArrayList<>();

    Collections.sort(a1);
    Collections.sort(a2);

    for(int i = 0; i < a1.size(); i++) {
      char c1 = a1.get(i);
      for (int j = 0; j <a2.size(); j++) {
        char c2 = a2.get(j);
        if (c1 == c2) {
          ret.add(c1);
          a1.remove(i);
          a2.remove(j);
          break;
        }
      }
    }

    return ret;
  }

  private ArrayList<String> prunikStringu(ArrayList<String> a1, ArrayList<String> a2) {
    ArrayList<String> ret = new ArrayList<>();

    Collections.sort(a1);
    Collections.sort(a2);

    for (int i = 0; i < a1.size(); i++) {
      String c1 = a1.get(i);
      for (int j = 0; j < a2.size(); j++) {
        String c2 = a2.get(j);
        if (c1.equals(c2)) {
          ret.add(c1);
          a2.remove(j);
          break;
        }
      }
    }

    return ret;
  }

  private String dejRadek(int cisloRadku, String retezec) {
    String[] lines = retezec.split("\r\n|\r|\n");
    String radek = null;
    if (lines.length >= cisloRadku) {
      radek = lines[--cisloRadku];
    }
    if (radek == null) {
      return null;
    }
    String ret = "";
    for(char c : radek.toCharArray()) {
      if (Character.isLetter(c))
        ret += c;
    }
    return ret;
  }

  private double mncLnLi() {
    double sum = 0;
    for (int i  = 1; i <= prumerySi.size(); i++) {
      sum += Math.log(i);
    }
    return sum;
  }

  private double mncLnLi2() {
    double sum = 0;
    for (int i = 1; i <= prumerySi.size(); i++) {
      double log = Math.log(i);
      sum += Math.pow(log, 2);
    }
    return sum;
  }

  private double mncLnSi() {
    double sum = 0;
    for (double d : prumerySi) {
      sum += Math.log(d);
    }
    return sum;
  }

  private double mncLnLiLnSi() {
    double sum = 0;
    for (int i = 1; i <= prumerySi.size(); i++) {
      double clen = Math.log(i);
      clen *= Math.log(prumerySi.get(i-1));
      sum += clen;
    }
    return sum;
  }

  public double getKoeficientD() {
    return koeficientD;
  }

  public ArrayList<Double> getTeoretickeSi() {
    return teoretickeSi;
  }

  public ArrayList<Double> getPrumerySi() {
    return prumerySi;
  }

  public double getMncB() {
    return mncB;
  }

  public double getMncA() {
    return mncA;
  }
}
